#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "vm.h"
#include "queue.h"
#include "dbg.h"

#define QUANTUM 10

list rdyQ; // queue for ready processes
list* sndQ; // queue for sending processes
list* rcvQ; // queue for receiving processes

int execute(brain10* vm, FILE* fileStream, int quantum);

int main(int argc, char* argv[]) {

	FILE* fileStream;
	brain10 vm;
	reset(&vm); //initialize register values

	/*if a file was passed from command line*/
	if (argc == 2) {
		fileStream = loadProgram(&vm, argv[1], &rdyQ);
	} else {
		char file[50];
		printf("Filename of brain program: ");
		scanf("%s", file);
		fileStream = loadProgram(&vm, file, &rdyQ);
	}

	/* initialize mailboxes */
	int len = rdyQ.length+1;
	sndQ = (list*) malloc((sizeof(list)*(rdyQ.length+1)));
	rcvQ = (list*) malloc((sizeof(list)*(rdyQ.length+1)));



	/* main execution loop */
	int switches = 0;
	while (rdyQ.length > 0) {
#ifdef DEBUG
		printf("\n\n\n****** context switch *******\n");
#endif
		loadContext(&vm, rdyQ.head->data);
		execute(&vm, fileStream, QUANTUM);
#ifdef DEBUG
		printf("Ready Que: \n");
		printList(&rdyQ);
		printf("Send Que: \n");
		int i;
		for(i = 0; i < len; i++){
			printf("mailbox %i  ",i);
			printList(&sndQ[i]);
		}
		printf("Rcv Que: \n");
		for(i = 0; i < len; i++){
			printf("mailbox %i  ",i);
			printList(&rcvQ[i]);
		}
#endif

		switches++;
	}

	printf("Finished with %i context switches with a %i quantum\n",switches,QUANTUM);
	return 0;
}

int execute(brain10* vm, FILE* fileStream, int quantum) {



	int ic = atoi(vm->IC);
	char instruction[3];
	int offset = atoi(vm->BR);

	int i;
	for (i = 0; i < quantum; i++) {

		/* get the instruction */
		instruction[2] = '\0';
		strncpy(instruction, vm->memory[ic + offset], 2);

		/* get the args */
		char args[3] = { vm->memory[ic + offset][2], vm->memory[ic + offset][3], '\0' };
		int x1x2 = atoi(args);


#if defined(DEBUG) && defined(VERBOSE)
		/* dump registers */
		printf("Instruction : %s\n", instruction);
		printf("Operands    : %s\n", args);

#endif

		if (strncmp(instruction, "LR", 2) == 0) {
			LR(vm, x1x2);
		} else if (strncmp(instruction, "LL", 2) == 0) {
			LL(vm, x1x2);
		} else if (strncmp(instruction, "LH", 2) == 0) {
			LH(vm, x1x2);
		} else if (strncmp(instruction, "SR", 2) == 0) {
			SR(vm, x1x2);
		} else if (strncmp(instruction, "SP", 2) == 0) {
			SP(vm);
		} else if (strncmp(instruction, "PS", 2) == 0) {
			PS(vm);
		} else if (strncmp(instruction, "PH", 2) == 0) {
			PH(vm);
		} else if (strncmp(instruction, "PP", 2) == 0) {
			PP(vm);
		} else if (strncmp(instruction, "CE", 2) == 0) {
			CE(vm, x1x2);
		} else if (strncmp(instruction, "CL", 2) == 0) {
			CL(vm, x1x2);
		} else if (strncmp(instruction, "BT", 2) == 0) {
			if (BT(vm, args)) {
				ic = atoi(vm->IC) - 1;
			}
		} else if (strncmp(instruction, "BU", 2) == 0) {
			BU(vm, args);
			ic = atoi(vm->IC) - 1;
		} else if (strncmp(instruction, "GD", 2) == 0) {
			GD(vm, x1x2, fileStream);
		} else if (strncmp(instruction, "PD", 2) == 0) {
			PD(vm, x1x2);
		} else if (strncmp(instruction, "AD", 2) == 0) {
			AD(vm, x1x2);
		} else if (strncmp(instruction, "SU", 2) == 0) {
			SU(vm, x1x2);
		} else if (strncmp(instruction, "MU", 2) == 0) {
			MU(vm, x1x2);
		} else if (strncmp(instruction, "DI", 2) == 0) {
			DI(vm, x1x2);
		} else if (strncmp(instruction, "AS", 2) == 0) {
			AS(vm);
		} else if (strncmp(instruction, "SS", 2) == 0) {
			SS(vm);
		} else if (strncmp(instruction, "MS", 2) == 0) {
			MS(vm);
		} else if (strncmp(instruction, "DS", 2) == 0) {
			DS(vm);
		} else if (strncmp(instruction, "NP", 2) == 0) {

		} else if (strncmp(instruction, "HA", 2) == 0) {
#ifdef DEBUG
			printf("***********************************\n");
			printf("*     Final State: Process %s     *\n",rdyQ.head->data->PID);
			printf("***********************************\n");
			print(vm);
#endif
			pop(&rdyQ);
			break;
		} else if (strncmp(instruction, "SD", 2) == 0) {

			snprintf(vm->IC, 3, "%.2i", ic+1); //update register
			storeContext(rdyQ.head->data, vm);
			if(SD(vm,sndQ,rcvQ,&rdyQ,args)) // 1 if the message was not rdy
				break;

		} else if (strncmp(instruction, "RC", 2) == 0) {

			snprintf(vm->IC, 3, "%.2i", ic+1); //update register
			storeContext(rdyQ.head->data, vm);
			if(RC(vm,sndQ,rcvQ,&rdyQ,args)) // 1 if the message was not rdy
				break;

		} else if (strncmp(instruction, "GP", 2) == 0) {
			GP(vm);
		} else {
			printf("Instruction %s not recognized, exiting",instruction);
			exit(1);
		}

#if defined(DEBUG) && defined(VERBOSE)
		print(vm);
#endif
		snprintf(vm->IC, 3, "%.2i", ++ic); //update register

		if(i+1 == quantum){
			storeContext(rdyQ.head->data, vm);
			cycle(&rdyQ);
		}

	}


	return 0;
}
